package com.noobyang.share;

import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * 内网客户端，要进行穿透的内网服务
 *
 * @author ln
 *
 */
public class Client {

    //首次与外网主机通信的连接
    private Socket socket;
    //首次与外网主机通信的本地端口
    private int localPort;

    private OutputStreamWriter outputStreamWriter;
    private BufferedReader bufferedReader;

    public static void main(String[] args) {
        new Client().start();
    }

    public void start() {
        try {
            // 新建一个socket通道
            socket = new Socket();
            // 设置reuseAddress为true
            socket.setReuseAddress(true);

            //TODO在此输入外网地址和端口
            String ip = "62.234.183.50";
            int port = 8888;
            socket.connect(new InetSocketAddress(ip, port));

            //首次与外网服务器通信的端口
            //这就意味着我们内网服务要与其他内网主机通信，就可以利用这个通道
            localPort = socket.getLocalPort();

            System.out.println("1. Client local PORT: " + localPort);

            outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());
            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            while (true) {
                process();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(outputStreamWriter != null) {
                    outputStreamWriter.close();
                }
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /*
     * 处理与服务器连接的交互，返回是否退出
     */
    private void process() throws IOException {
        String in = null;
        //获取服务器的响应信息
        String info = bufferedReader.readLine();
        System.out.println("2. Client read from server: " + info);
        if(info != null) {
            if (info.startsWith("autoConnect_")) {
                System.out.println("3. Client Auto connect target: " + info);

                String[] infos = info.split("_");
                //目标外网地址
                String ip = infos[1];
                //目标外网端口
                String port = infos[2];

                doPenetration(ip, Integer.parseInt(port));
            } else {
                String[] infos = info.split(";");
                System.out.println("3. Client infos 0:" + infos[0]);

                if (infos.length > 1) {
                    System.out.println("3. Client infos 1:" + infos[1]);
                    String ip = infos[0].split("_")[0];
                    int port = Integer.parseInt(infos[0].split("_")[1]);

                    String targetIP = infos[1].split("_")[0];
                    int targetPort = Integer.parseInt(infos[1].split("_")[1]);

                    outputStreamWriter.write("connect_" + targetIP + "_" + targetPort + "\n");
                    outputStreamWriter.flush();

                    doPenetration(targetIP, targetPort);
                }
            }
        }
    }

    /*
     * 对目标服务器进行穿透
     */
    private void doPenetration(String ip, int port) {
        try {
            //异步对目标发起连接
            new Thread() {
                public void run() {
                    try {

                        Socket newsocket = new Socket();

                        newsocket.setReuseAddress(true);
                        newsocket.bind(new InetSocketAddress(
                                InetAddress.getLocalHost().getHostAddress(), localPort));

                        System.out.println("4. Client connect to: " + new InetSocketAddress(ip, port));

                        newsocket.connect(new InetSocketAddress(ip, port));

                        System.out.println("5. Client connect success");

                        BufferedReader b = new BufferedReader(
                                new InputStreamReader(newsocket.getInputStream()));
                        PrintWriter p = new PrintWriter(newsocket.getOutputStream());

                        while (true) {

                            p.write("hello " + System.currentTimeMillis() + "\n");
                            p.flush();

                            String message = b.readLine();

                            System.out.println(message);

                            outputStreamWriter.write(message + "\n");
                            outputStreamWriter.flush();

                            if("exit".equals(message)) {
                                break;
                            }

                            Thread.sleep(1000l);
                        }

                        b.close();
                        p.close();
                        newsocket.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
